package com.ruoyi.framework.web.service;

import java.lang.reflect.Method;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ruoyi.common.annotation.ApiEncrypt;
import com.ruoyi.common.utils.security.ApiEncryptUtils;

/**
 * API响应加密处理
 * 
 * @author ruoyi
 */
@ControllerAdvice
public class ApiEncryptResponseBodyAdvice implements ResponseBodyAdvice<Object>
{
    private static final Logger log = LoggerFactory.getLogger(ApiEncryptResponseBodyAdvice.class);
    private final ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType)
    {
        // 获取方法
        Method method = returnType.getMethod();
        if (method == null)
        {
            log.debug("方法为空，不进行加密处理");
            return false;
        }
        
        // 获取类上的注解
        ApiEncrypt classAnnotation = returnType.getContainingClass().getAnnotation(ApiEncrypt.class);
        // 获取方法上的注解
        ApiEncrypt methodAnnotation = method.getAnnotation(ApiEncrypt.class);
        
        // 如果方法和类上都没有注解，则不需要加密
        if (classAnnotation == null && methodAnnotation == null)
        {
            log.debug("类和方法上都没有@ApiEncrypt注解，不进行加密处理");
            return false;
        }
        
        // 检查是否为GET请求或文件上传请求，这些请求不进行响应加密
        // 通过RequestContextHolder获取当前请求信息
        try {
            org.springframework.web.context.request.RequestAttributes requestAttributes = 
                org.springframework.web.context.request.RequestContextHolder.getRequestAttributes();
            if (requestAttributes != null) {
                javax.servlet.http.HttpServletRequest request = 
                    ((org.springframework.web.context.request.ServletRequestAttributes) requestAttributes).getRequest();
                
                // GET请求不进行响应加密
                if ("GET".equalsIgnoreCase(request.getMethod())) {
                    log.debug("GET请求不进行响应加密: {}", request.getRequestURI());
                    return false;
                }
                
                // 文件上传请求不进行响应加密
                String contentType = request.getContentType();
                if (contentType != null && contentType.toLowerCase().contains("multipart/form-data")) {
                    log.debug("文件上传请求不进行响应加密: {}", request.getRequestURI());
                    return false;
                }
            }
        } catch (Exception e) {
            log.warn("获取请求信息失败，继续原有逻辑: {}", e.getMessage());
        }
        
        // 优先使用方法上的注解，如果方法上没有注解，则使用类上的注解
        boolean shouldEncrypt = methodAnnotation != null ? methodAnnotation.responseEncrypt() : classAnnotation.responseEncrypt();
        log.debug("响应加密判断结果: {}, 方法: {}", shouldEncrypt, method.getName());
        return shouldEncrypt;
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
            Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,
            ServerHttpResponse response)
    {
        log.debug("开始处理响应加密，请求URI: {}, 响应体类型: {}", request.getURI(), body != null ? body.getClass().getSimpleName() : "null");
        
        try
        {
            // 将响应对象转为JSON字符串
            String jsonString = objectMapper.writeValueAsString(body);
            log.debug("原始响应JSON: {}", jsonString);
            
            // 加密JSON字符串
            String encryptedData = ApiEncryptUtils.encrypt(jsonString);
            log.debug("加密后数据长度: {}", encryptedData.length());
            
            // 设置响应类型为纯文本
            response.getHeaders().setContentType(MediaType.TEXT_PLAIN);
            log.debug("响应加密处理完成");
            
            // 返回加密后的数据
            return encryptedData;
        }
        catch (Exception e)
        {
            log.error("响应加密失败: {}", e.getMessage(), e);
            // 加密失败，返回原始数据
            return body;
        }
    }
}